package org.lgiavedoni.android.franq.core.question;

import lombok.Data;

import org.lgiavedoni.android.franq.core.dao.DaoObject;
import org.lgiavedoni.android.franq.core.exception.ControledFinishException;
import org.lgiavedoni.android.franq.core.message.MessageIn;
import org.lgiavedoni.android.franq.service.communication.CommunicationService;
import org.lgiavedoni.android.franq.service.config.ConfigService;
import org.lgiavedoni.android.franq.service.config.ConfigService.ConfigKey;
import org.lgiavedoni.android.franq.service.log.Logger;
import org.lgiavedoni.android.franq.util.date.DateUtil;

@Data
public abstract class Question<result extends Object> extends DaoObject {
	private Object result = null;
	private boolean solved = false;
	private boolean discarded = false;
	private final String QUESTION_FLAG;
	private int iteration = 0;

	public Question() {
		super();
		QUESTION_FLAG = "FLAG_" + DateUtil.getTimeInMillis();
	}

	public abstract String getQuestionText();

	public void ask() {
		discarded = false;
		if (!isSolved()) {
			CommunicationService.inst.sendDefaultTextMessage(getQuestionText());
			getResult();
		} else {
			Logger.inst.warning("This question [" + this + "] is already solved.");
		}
	}

	/**
	 * @param userInfo
	 * @return true if the question was solved
	 */
	public abstract boolean process(MessageIn messageIn);

	private void end() {
		solved = true;
		discarded = false;
	}

	public result getResultValue() {
		return result != null ? (result) result : null;
	}

	private synchronized int generateNewIteration() {
		return ++iteration;
	}

	private Object getResult() {
		if (!isSolved()) {
			try {
				int internalIteraion = generateNewIteration();

				synchronized (QUESTION_FLAG) {
					QUESTION_FLAG.wait(ConfigService.inst.getRandomValueAsLong(ConfigKey.QUESTION_TIMEOUT));

					// Check if I am not the last iteration
					if (internalIteraion != getIteration()) {
						// Are you the main thread?
						if (internalIteraion == 1) {
							// At the moment don't have sense for me
							// flag.wait();
						} else {
							// You are not the first and not the last, you are
							// nothing
							throw new ControledFinishException();
						}
					}
					// check for the timeout
					if (result == null) {
						discarded = true;
						return null;
					}
				}
			} catch (Exception e) {
				if (e instanceof ControledFinishException)
					throw (RuntimeException) e;
				Logger.inst.error(e);
			}
		}
		return result;
	}

	public void cancel() {
		synchronized (QUESTION_FLAG) {
			this.result = null;
			// end();
			discarded = true;
			QUESTION_FLAG.notifyAll();
		}
	}

	protected void setResult(Object result) {
		/*
		 * Object tmp = this.result; synchronized (tmp) {
		 */
		synchronized (QUESTION_FLAG) {
			this.result = result;
			end();
			// tmp.notifyAll();
			QUESTION_FLAG.notifyAll();
		}

	}

	@Override
	public boolean equals(Object obj) {
		if (obj instanceof Question)
			return equals((Question) obj);
		return false;
	}

	public abstract boolean equals(Question obj);

}
